package com.zkh.myframe.core;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.zkh.myframe.annotation.Arguments;
import com.zkh.myframe.annotation.RequestMethod;
import com.zkh.myframe.exception.BaseException;
import com.zkh.myframe.validate.ParamValidator;
import com.zkh.myutils.code.Charsets;
import com.zkh.myutils.code.CoderUtils;
import com.zkh.myutils.io.file.FileUtils;
import com.zkh.myutils.io.log.Logger;
import com.zkh.myutils.net.upload.Uploader;
import com.zkh.myutils.utils.StringUtils;
import com.zkh.myutils.utils.UtilsException;

/**
 * 链对象，本框架核心方法之一
 * @author zkh
 */
public class Chain {
	//计次
	private int count = 0;
	
	//一系列属性
	private HttpServletRequest request;
	private HttpServletResponse response;
	private List<Class<?>> chains;
	private String[] controllerName;
	
	/**
	 * 构造函数
	 * @param request ServletRequest对象
	 * @param response ServletResponse对象
	 * @param interceptorChains 拦截器链
	 * @param controllerName [urlPattern, methodsAndArguments]
	 */
	public Chain(ServletRequest request, ServletResponse response, List<Class<?>> interceptorChains, String[] controllerName){
		this.request = (HttpServletRequest) request;
		this.response = (HttpServletResponse) response;
		this.chains = interceptorChains;
		this.controllerName = controllerName;
	}
	
	/**
	 * 执行下一个拦截器
	 * @throws IOException 
	 */
	public void nextChain() throws Exception{
		//判断是否有拦截器，检查拦截器是否执行完毕，如果未执行完，则执行拦截器
		if(chains!=null && chains.size()>0 && count<chains.size()){
			//实例化拦截器
			try{
				//获取索引
				int index = count++;
				//获取拦截器中的doFilter方法
				Method method = chains.get(index).getMethod("doFilter", HttpServletRequest.class, HttpServletResponse.class, Chain.class);
				//执行方法
				method.invoke(chains.get(index).newInstance(), request, response, this);
			}catch(InvocationTargetException ITException){
				//目标异常
				Throwable te = ITException.getTargetException();
				//自定义业务异常
				if(te instanceof BaseException){
					//强转自定义业务异常
					BaseException e = (BaseException) te;
					//错误页面
					String excepPage = Configure.getBaseExceptionPage();
					//404
					if(e.getErrorCode()==404){
						response.sendError(404, "你请求的页面不存在");
					}else if(e.getErrorCode()!=999 && !StringUtils.isEmpty(excepPage)) {
						//错误信息
						request.setAttribute("e", e);
						//跳转到错误页面
						request.getRequestDispatcher(excepPage).forward(request, response);
					}else {
						throw e;
					}
				}else {
					throw te instanceof Exception ? (Exception) te : new Exception(te);
				}
			}
		}
		//拦截器已执行完毕，则执行应用
		else{
			controllerAccess(request, response, controllerName);
		}
	}
	
	/**
	 * 访问Controller的核心方法，由链对象自动调用
	 * @param request ServletRequest对象
	 * @param response ServletResponse对象
	 * @param controllerName URLPattern和Controller的ClassName
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private void controllerAccess(HttpServletRequest request, HttpServletResponse response, String[] controllerName) throws Exception{
		//获取URI
		String uri = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("//+", "/");
		//检查是否是首页访问，且有无首页拦截
		if("/".equals(uri) && !Configure.getFirstPage().isEmpty()){
			uri = Configure.getFirstPage();
		}
		//检查并调整url的后缀
		uri = checkUrlSuffix(uri);
		//获取URLPattern
		String urlPattern = controllerName[0];
		
		//获取方法，默认调用index方法
		String method = "index";
		//获取方法名及其后面的参数
		String methods = CoderUtils.decodeURL(uri.substring(urlPattern.length()), Configure.getGlobalCharset());
		//参数集合
		List<String> args = new ArrayList<String>();
		//判断pattern以*号结尾
		if(methods.length()>1){
			//去掉方法参数前的/
			methods = methods.substring(1);
			//获取方法和参数集合
			args.addAll(Arrays.asList(methods.split("/")));
			//获取方法名
			method = args.get(0);
			//移除方法名，剩余的都是参数
			args.remove(0);
		}
		
		//使用反射调用方法，方法参数为Adapter适配器类
		try {
			//实例化适配器类
			Adapter<ParamValidator> adapter = new Adapter<ParamValidator>(request, response);
			//Controller的Class
			Class<?> clazz = Class.forName(controllerName[1]);
			//获取Controller实例
			Object instance = FrameUtils.getResourceClass(clazz, Configure.getResourceSingleton());
			//保存HttpServletRequest和HttpServletResponse对象
			FrameUtils.getServletRequestContainer().set(request);
			FrameUtils.getServletResponseContainer().set(response);
			/**↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 使用反射获取请求主方法(开始) ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓**/
			//记录返回值
			Object rtnValue = null;
			try {
				//获取反射方法对象
				Method fMethod = clazz.getMethod(method, Adapter.class);
				//请求方式注解
				RequestMethod requestMethod = fMethod.getAnnotation(RequestMethod.class);
				//存在注解
				if(requestMethod!=null) {
					//与设置的请求方式不相同，返回404
					if(!requestMethod.method().equalsIgnoreCase(request.getMethod())) {
						adapter.setResult(404);
						//处理后续步骤，如：跳转，重定向，发送HTML或返送JSON数据等
						adapter.doResult();return;
					}
				}
				//解析方法上的参数注解。
				if(args.size()>0){
					parseAnnotation(fMethod, args, adapter);
				}
				//格式化参数
				formatAguments(adapter);
				//获取参数类型以获取泛型类型
				Type type = fMethod.getParameters()[0].getParameterizedType();
				//验证器返回JSON
				boolean rtnJSON = false;
				//使用了泛型
				if(type instanceof ParameterizedType) {
					//泛型类型
					String geneType = ((ParameterizedType) type).getActualTypeArguments()[0].getTypeName();
					//不等于？
					if(!"?".equals(geneType)) {
						//初始化泛型
						adapter.setParamValidatorClass((Class<ParamValidator>) Class.forName(geneType));
						//转换参数并验证
						try {
							//参数赋值并执行验证器检查
							adapter.validateErrorMsg = ValidatorChecker.execParamValidatorCheck(adapter);
						}catch(Exception e) {
							//打印异常日志
							Logger.error(e);
							//异常消息
							String errorMsg = (e instanceof UtilsException ? "" : "转换并验证参数时出现错误：") + e.getMessage();
							//保存异常
							adapter.validateErrorMsg.put("otherError", errorMsg);
						}
						//POST请求，返回JSON
						if(adapter.isTargetMethod("POST") && !adapter.getParamValidateErrorMsg().isEmpty()) {
							//组合错误信息
							adapter.putMap("message", String.join("；", adapter.getParamValidateErrorMsg().values()));
							adapter.putMap("code", "9999");
							//保存返回的JSON对象
							rtnValue = adapter.getMap();
							//标注直接返回
							rtnJSON = true;
						}
					}
				}
				if(!rtnJSON) {
					//调用反射获得的方法
					rtnValue = fMethod.invoke(instance, adapter);
				}
			}catch(NoSuchMethodException e) {
				//获取反射方法对象
				Method fMethod = clazz.getMethod(method);
				//调用反射获得的方法（参数注解是把参数解析Adapter对象中，此处不传递Adapter对象，故不再需要解析参数）
				rtnValue = fMethod.invoke(instance);
			}
			/**↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 使用反射获取请求主方法(结束) ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑**/
			//如果有返回值，则以返回值为主，覆盖setResult保存的值
			if(rtnValue!=null) {
				adapter.setResult(rtnValue);
			}
			//处理后续步骤，如：跳转，重定向，发送HTML或返送JSON数据等
			adapter.doResult();
		} catch (NoSuchMethodException e) {
			//方法不存在，释放
			throw new BaseException(999);
		} catch (InvocationTargetException e) {
			//获取抛出的自定义异常
			Throwable ex = e.getTargetException();
			//错误页面
			String excepPage = Configure.getBaseExceptionPage();
			//BaseException
			if(ex instanceof BaseException && !StringUtils.isEmpty(excepPage)){
				//捕获自定义异常
				request.setAttribute("e", ex);
				request.getRequestDispatcher(excepPage).forward(request, response);
			}else {
				throw ex instanceof Exception ? (Exception) ex : new Exception(ex);
			}
		} finally {
			//移除HttpServletRequest和HttpServletResponse对象
			FrameUtils.getServletRequestContainer().remove();
			FrameUtils.getServletResponseContainer().remove();
		}
	}

	/**
	 * 格式化参数，解析流参数
	 * @param adapter
	 */
	private void formatAguments(Adapter<ParamValidator> adapter) {
		//过滤上传类型请求
		if(!Uploader.isUpload(adapter.getRequest())) {
			//获取流参数
			String stream = getStreamParam(adapter.getRequest());
			//判断是否满足JOSN格式
			if(StringUtils.isOuterMatched(stream, "{", "}") || StringUtils.isOuterMatched(stream, "[", "]")) {
				adapter.setArgumentJSONString(CoderUtils.decodeURL(stream));
			}
			//首先读取URL链接上的参数，优先级低于流
			adapter.getRequest().getParameterMap().forEach((key, values)->{
				//有值
				if(values!=null) {
					//处理key（数组参数去掉[]）
					key = key.endsWith("[]") ? key.substring(0, key.length()-2) : key;
					//没有值，给默认空字符串
					if(values.length==0) {
						adapter.addParameter(key, "");
					}//有值
					else {
						//遍历依次写入容器并解码
						for(String v: values) {
							adapter.addParameter(key, CoderUtils.decodeURL(v));
						}
					}
				}
			});
			//不是JSON，则手动解析。如果key相同，覆盖前面的参数
			if(!adapter.isJSONParam() && !stream.isEmpty()){
				//分割并遍历参数
				for(String kvStr: stream.split("&")) {
					//等号分隔符
					int idx = kvStr.indexOf('=');
					//存在
					if(idx>-1) {
						//key
						String key = CoderUtils.decodeURL(kvStr.substring(0, idx));
						//处理key
						key = key.endsWith("[]") ? key.substring(0, key.length()-2) : key;
						//保存值
						adapter.addParameter(key,  CoderUtils.decodeURL(kvStr.substring(idx+1)));
					}
				}
			}
		}
	}
	
	/**
	 * 读取流参数，不解码
	 * @param request 请求对象
	 * @return
	 */
	private String getStreamParam(HttpServletRequest request) {
		try {
			return FileUtils.readAll(request.getInputStream(), Charsets.UTF_8);
		} catch (IOException e) {
			throw new BaseException("参数读取失败", e);
		}
	}

	/**
	 * 检查URL后缀
	 * @param uri
	 */
	private String checkUrlSuffix(String uri) {
		//获取后缀配置
		String[] urlSuffixes = Configure.getUrlSuffixes();
		//遍历配置
		for(String urlSuffix:urlSuffixes){
			//如果以指定后缀结尾
			if(uri.endsWith(urlSuffix)){
				//去掉后缀
				return uri.substring(0, uri.indexOf(urlSuffix));
			}
		}
		return uri;
	}
	
	/**
	 * 解析参数注解
	 * @param method 调用的方法
	 * @param arguments 参数列表
	 * @param adapter 适配器，此处用来保存解析后的参数
	 */
	private void parseAnnotation(Method method, List<String> arguments, Adapter<ParamValidator> adapter){
		//获得注解的对象
        Arguments args = method.getAnnotation(Arguments.class);
        //如果存在注解对象，则解析注解
        if(args!=null){
        	//注解类型
        	int type = args.type();
        	//参数依次解析为数组
        	switch(type){
        		case Arguments.TYPE_NONAME:
        			//保存参数
        			adapter.setParameter("arguments_myframe", arguments);
        			break;
        		case Arguments.TYPE_WITHNAME:
                	//参数名
                	String[] argnames = args.argnames();
                	//实际长度和溢出量
                	int len = Math.min(argnames.length, arguments.size()), out = argnames.length - arguments.size();
                	//遍历公共长度
                	for(int i=0; i<len; i++) {
                		adapter.addParameter(argnames[i], arguments.get(i));
                	}
                	//若参数名称更长，未赋值的参数赋值为null
                	if(out>0) {
                		for(int i=len,offset=len+out; i<offset; i++) {
                			adapter.addParameter(argnames[i], null);
                		}
                	}
        			break;
        		case Arguments.TYPE_KEYVALUE:
        			//计数
        			int i=0, size=arguments.size();
        			//循环遍历
        			while(i<size-1){
        				//键和值
        				String key = arguments.get(i), value = arguments.get(i+1);
        				//添加值
        				adapter.addParameter(key, value);
        				//跳过已经保存的一对键和值
        				i += 2;
        			}
        			break;
        	}
        }else{
			//保存参数
			adapter.setParameter("arguments_myframe", arguments);
        }
	}
}
